package cn.tedu.mall.seckill.timer;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.pojo.seckill.model.SeckillSku;
import cn.tedu.mall.pojo.seckill.model.SeckillSpu;
import cn.tedu.mall.seckill.mapper.SeckillSkuMapper;
import cn.tedu.mall.seckill.mapper.SeckillSpuMapper;
import cn.tedu.mall.seckill.utils.SeckillCacheUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.math.RandomUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Slf4j
public class SeckillInitialJob implements Job {
    //先查询spu，获取sku列表，得到库存数
    @Autowired
    private SeckillSpuMapper seckillSpuMapper;
    @Autowired
    private SeckillSkuMapper seckillSkuMapper;
    //库存数需要保存到redis中，我们将来会直接操作redis中的数据库进行加减。可以避免超卖的情况发生
    //直接修改Redis中的数据，需要以字符串的形式保存到Redis中，使用StringRedisTemplate这个工具类
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    //我们还需保存随机码到redis中，随机码不需要进行修改，所以以二进制格式保存即可，这样操作读写速度快
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        //缓存预热，业务设计是在开始前5分钟开始预热
        //所以我们需要一个五分钟后的时间去检查秒杀是否开始
        LocalDateTime time = LocalDateTime.now().plusMinutes(5);
        //查询time时间的秒杀商品
        List<SeckillSpu> seckillSpus = seckillSpuMapper.findSeckillSpusByTime(time);
        //判断seckillSpus集合是否为null
        if(seckillSpus==null)
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"不在秒杀时间范围内，稍后预热");
        //开始缓存预热
        //遍历seckillSpus集合，根据spuId获取sku列表，进行缓存预热
        for (SeckillSpu seckillSpu : seckillSpus) {
            Long spuId = seckillSpu.getSpuId();
            List<SeckillSku> seckillSkus = seckillSkuMapper.findSeckillSkusBySpuId(spuId);
            //取出seckillSku中的库存数，进行预热
            for (SeckillSku seckillSku : seckillSkus) {
                Long skuId = seckillSku.getSkuId();
                log.info("开始将{}号商品的库存数预热到Redis中",skuId);
                //根据skuId获取到对应的key的名称
                String stockKey = SeckillCacheUtils.getStockKey(skuId);
                //检查redis中是否包含该key
                if(redisTemplate.hasKey(stockKey)){
                    log.info("{}号商品已经在缓存中了",skuId);
                }else{
                    //如果key不存在，进行缓存
                    stringRedisTemplate.boundValueOps(stockKey).set(seckillSku.getSeckillStock()+"",
                            24*60*60+ RandomUtils.nextInt(100), TimeUnit.SECONDS);
                    log.info("成功为{}号商品添加库存数",skuId);
                }
            }
            //生成随机码进行缓存
            //获取随机码的key值
            String randCodeKey = SeckillCacheUtils.getRandCodeKey(spuId);
            //判断随机码是否已经生成
            if(redisTemplate.hasKey(randCodeKey)){
                //如果随机码已经存在，取出来，方便测试，实际上并不需要取出
               int randCode = (int)redisTemplate.boundValueOps(randCodeKey).get();
               log.info("spuId为{}的商品的随机码为{}",spuId,randCode);
            }else{
                //生成随机码进行缓存
              int randCode = RandomUtils.nextInt(900000) + 100000;
              redisTemplate.boundValueOps(randCodeKey)
                      .set(randCode,24*60*60+RandomUtils.nextInt(100),TimeUnit.SECONDS);
              log.info("成功为spuId为{}的商品生产随机码，随机码为{}",spuId,randCode);
            }
        }
    }
}
